package com.easipass.framework.eirframeworkcoredsl;

import java.util.Collections;
import java.util.List;

public class Tokens {

    private final String expression;

    private final List<Token> tokenService;

    private int[] linebreaks;

    private int position = 0;

    private int lastGoodPosition = 0;

    Tokens(String expression, List<Token> tokens, int[] linebreaks) {
        this.expression = expression;
        this.linebreaks = linebreaks;
        this.tokenService = Collections.unmodifiableList(tokens);
    }

    protected String getExpression() {
        return expression;
    }

    protected int decrementPosition() {
        return --position;
    }

    protected int position() {
        return position;
    }

    public List<Token> getTokenService() {
        return tokenService;
    }

    protected boolean lookAhead(int distance, TokenKind desiredTokenKind) {
        if ((position + distance) >= tokenService.size()) {
            return false;
        }
        Token t = tokenService.get(position + distance);
        return t.kind == desiredTokenKind;
    }

    protected boolean hasNext() {
        return position < tokenService.size();
    }

    protected Token peek() {
        return hasNext() ? tokenService.get(position) : null;
    }

    protected Token peek(int howFarAhead) {
        if ((position + howFarAhead) >= 0 && (position + howFarAhead) < tokenService.size()) {
            return tokenService.get(position + howFarAhead);
        } else {
            return null;
        }
    }

    protected boolean peek(TokenKind desiredTokenKind) {
        return peek(desiredTokenKind, false);
    }

    private boolean peek(TokenKind desiredTokenKind, boolean consumeIfMatched) {
        if (!hasNext()) {
            return false;
        }
        Token t = peek();
        if (t.kind == desiredTokenKind) {
            if (consumeIfMatched) {
                position++;
            }
            return true;
        } else {
            return false;
        }
    }

    protected Token next() {
        if (!hasNext()) {
            raiseException(expression.length(), DSLMessage.OOD);
        }
        return tokenService.get(position++);
    }

    protected Token eat(TokenKind expectedKind) {
        Token t = next();
        if (t == null) {
            raiseException(expression.length(), DSLMessage.OOD);
        }
        if (t.kind != expectedKind) {
            raiseException(t.startPos, DSLMessage.NOT_EXPECTED_TOKEN, expectedKind.toString().toLowerCase(),
                    (t.data == null) ? new String(t.getKind().tokenChars).toLowerCase() : t.data);
        }
        return t;
    }

    protected Token eat() {
        Token t = next();
        if (t == null) {
            raiseException(expression.length(), DSLMessage.OOD);
        }
        return t;
    }

    protected boolean isNextAdjacent() {
        if (!hasNext()) {
            return false;
        }
        Token last = tokenService.get(position - 1);
        Token next = tokenService.get(position);
        return next.startPos == last.endPos;
    }

    protected void checkpoint() {
        lastGoodPosition = position;
    }

    protected void raiseException(int position, DSLMessage message, Object... inserts) {
        throw new CheckPointedParseException(expression, position, this.position, lastGoodPosition, tokenService,
                message, inserts);
    }

    public int getLine(Token token) {
        int tokenStart = token.startPos;
        int lb = 0;
        while (lb < linebreaks.length && linebreaks[lb] < tokenStart) {
            lb++;
        }
        return lb;
    }
}
